package com.example.curvaturetest;

import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;

import androidx.annotation.Nullable;

public class CurvatureView extends View {
    private final Context context;

    public CurvatureView(Context context) {
        this(context, null);
    }

    public CurvatureView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public CurvatureView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        this.context = context;
        init();
    }


    /**
     * 展示的x坐标
     */
    double ovalX = 0;
    /**
     * 展示的y坐标
     */
    double ovalY;
    /**
     * 椭圆方程一阶导
     */
    double fY;
    /**
     * 椭圆方程二阶导
     */
    double sY;
    /**
     * 曲率圆半径
     */
    double cR;
    /**
     * 曲率圆圆心x坐标
     */
    double cA;
    /**
     * 曲率圆圆心y坐标
     */
    double cB;
    /**
     * 短半轴
     */
    float semiMinorAxis = 300f;
    /**
     * 长半轴
     */
    float semiMajorAxis = 400f;
    /**
     * 椭圆圆心x
     */
    float oX;
    /**
     * 椭圆圆心y
     */
    float oY;


    Path ovalPath = new Path();
    RectF rect = new RectF();

    private final Paint ovalPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    private final Paint circlePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    private final Paint pointPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    private Bitmap earthBitmap;
    private Bitmap sunBitmap;


    private void init() {
        //设置画笔
        ovalPaint.setStyle(Paint.Style.STROKE);
        ovalPaint.setStrokeWidth(3);
        ovalPaint.setARGB(255, 45, 64, 70);
        circlePaint.setStyle(Paint.Style.STROKE);
        circlePaint.setStrokeWidth(2);
        circlePaint.setARGB(255, 153, 171, 177);
        pointPaint.setStyle(Paint.Style.FILL_AND_STROKE);
        pointPaint.setStrokeWidth(5);
        pointPaint.setARGB(255, 153, 171, 177);


        //获取屏幕宽高，确定原点，框定椭圆
        DisplayMetrics dm = context.getResources().getDisplayMetrics();
        oX = (float) dm.widthPixels / 2;
        oY = (float) dm.heightPixels / 2;
        rect.set(oX - semiMajorAxis,
                oY - semiMinorAxis,
                oX + semiMajorAxis,
                oY + semiMinorAxis);


        //加载位图
        earthBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ear);
        earthBitmap = scaleBitmap(earthBitmap, 0.1f);
        sunBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.su);
        sunBitmap = scaleBitmap(sunBitmap, 0.22f);


    }


    private void calculateAll(int state) {
        ovalY = getOvalY(ovalX, state);
        fY = getFirstDerivativeY(ovalX, ovalY);
        sY = getSecondDerivativeY(ovalX, ovalY, fY);
        cR = getCurvatureRadius(fY, sY);
        cA = getCurvatureCircleX(ovalX, fY, sY);
        cB = getCurvatureCircleY(ovalY, fY, sY);
    }


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        //画椭圆
        ovalPath.addOval(rect, Path.Direction.CCW);
        canvas.drawPath(ovalPath, ovalPaint);

        //画地球
        float centerX = (float) ovalX + oX;
        float centerY = oY - (float) ovalY;
        int w1 = earthBitmap.getWidth();
        int h1 = earthBitmap.getHeight();
        canvas.drawBitmap(earthBitmap, centerX - (float) w1 / 2, centerY - (float) h1 / 2, ovalPaint);

        //画太阳
        double c = Math.sqrt(semiMajorAxis * semiMajorAxis - semiMinorAxis * semiMinorAxis);
        int w2 = sunBitmap.getWidth();
        int h2 = sunBitmap.getHeight();
        canvas.drawBitmap(sunBitmap, oX + (float) c - (float) w2 / 2, oY - (float) h2 / 2, ovalPaint);

        //画曲率圆
        float cX = (float) cA + oX;
        float cY = oY - (float) cB;
        canvas.drawCircle(cX, cY, (float) cR, circlePaint);
        canvas.drawPoint(cX, cY, pointPaint);

        invalidate();
    }


    @SuppressLint("ClickableViewAccessibility")
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_MOVE) {
            float dstX = event.getX() - oX;
            boolean isIn = (dstX > -semiMajorAxis) && (dstX < semiMajorAxis);
            if (isIn && event.getY() > oY) {
                ovalX = dstX;
                calculateAll(-1);
            } else if (isIn && event.getY() < oY) {
                ovalX = dstX;
                calculateAll(1);
            } else if (dstX >= semiMajorAxis) {
                ovalX = semiMajorAxis - 0.1;
                calculateAll(1);
            } else if (dstX <= -semiMajorAxis) {
                ovalX = -semiMajorAxis + 0.1;
                calculateAll(-1);
            }
        }

        return true;
    }

    //以下为计算公式
    private double getCurvatureCircleY(double ovalY, double fY, double sY) {
        double s1 = 1 + fY * fY;
        return ovalY + s1 / sY;
    }

    private double getCurvatureCircleX(double ovalX, double fY, double sY) {
        double s1 = fY * (1 + fY * fY);
        return ovalX - s1 / sY;
    }

    private double getCurvatureRadius(double fY, double sY) {
        double s1 = 1 + fY * fY;
        double s3 = Math.abs(sY);
        double s2 = Math.pow(s1, 1.5);
        return (s2 / s3);
    }

    private double getSecondDerivativeY(double ovalX, double ovalY, double fY) {
        double s1 = (ovalY - ovalX * fY) / (ovalY * ovalY);
        double s2 = -(semiMinorAxis * semiMinorAxis) / (semiMajorAxis * semiMajorAxis);
        return s1 * s2;
    }

    private double getFirstDerivativeY(double ovalX, double ovalY) {
        double s1 = ovalX / ovalY;
        double s2 = -(semiMinorAxis * semiMinorAxis) / (semiMajorAxis * semiMajorAxis);
        return s1 * s2;
    }

    private double getOvalY(double ovalX, int state) {
        float a = semiMajorAxis;
        float b = semiMinorAxis;
        double h = Math.sqrt(1 - ((ovalX * ovalX) / (a * a)));
        return state * b * h;

    }

    private Bitmap scaleBitmap(Bitmap bitmap, float s) {
        Matrix matrix = new Matrix();
        matrix.postScale(s, s);//长宽缩放比例
        Bitmap resizeBmp;
        resizeBmp = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
        return resizeBmp;
    }


}
